iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

在 Quarkus OIDC 的整合中,主要分成會分成 web-app 與 service 兩種 type。應用 OIDC 授權流程可以保護我們的 HTTP 端點,並且利用到 OIDC 的授權伺服器例如 Keycloak.

Web-App OIDC Auth Code Flow

web-app 模式下,啟用 OIDC 的授權碼流程機制( Authorization Code Flow mechanism )時,使用者在未登入情況下,當遇到需要權限的頁面時,會遇到以下流程

  1. 使用者瀏覽一個 quarkus 應用頁面
  2. Quarkus AP 把 user 導到 OIDC 的提供方的頁面登入
  3. 使用者輸入必要的驗證
  4. 登入後,OIDC 提供方會再回本來的 access 的頁面並帶著授權碼 (Authorization Code) 表示驗證成功。
  5. Quarkus AP 會拿這個授權碼再去換回 ID Token, Access Token, 以及 Refresh token

如下圖所示

實作目標

今天會作一個 /api/users/me 來取得目前的使用者,並整合 keycloak 授權碼流程機制。

project 放置於此

首先設定 quarkus oidc properties


%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
# hybrid 表示 web-app, service 兩種通吃
quarkus.oidc.application-type=hybrid
# 所有 endpont 都要驗證 /*
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
# Tell Dev Services for Keycloak to import the realm file
quarkus.keycloak.devservices.realm-path=quarkus-realm.json

建立 Token Resource

在 /src/main/kotlin/.../resource 建立 TokenResource.kt

import io.quarkus.oidc.IdToken
import org.eclipse.microprofile.jwt.JsonWebToken
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces

@Path("/api/tokens")
class TokenResource {
    @Inject
    @IdToken
    lateinit var idToken: JsonWebToken

    @Inject
    lateinit var accessToken: JsonWebToken

    @Produces("text/html")
    @GET
    fun getTokens() = "<html><body><h3>idToken</h3>$idToken" +
        "<h3>accessToken</h3>$accessToken</body></html>"
}

拜訪 /api/token

http://localhost:8080/api/tokens , 會被自動轉打到 keycloak ,並且後面有 redirect url

登入後可以看到印出 token 內容

建立 /api/user/me

在 /src/main/kotlin/.../resource 建立 UsersResource.kt 並輸入以下內容

package tw.brandy.ironman.resource

import io.quarkus.oidc.IdToken
import org.eclipse.microprofile.jwt.JsonWebToken
import org.jboss.resteasy.reactive.NoCache
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path

@Path("/api/users")
class UsersResource {

    @Inject
    lateinit var idToken: JsonWebToken

    @GET
    @Path("/me")
    @NoCache
    fun me(): User {
        return User(idToken)
    }
}

class User internal constructor(idToken: JsonWebToken) {
    val userName: String
    init {
        userName = idToken.name
    }
}

拜訪 /api/users/me

http://localhost:8080/api/users/me 會看到帶出目前使用者

RestAssumed Test with User

properites 要加上三條以供 test

%test.quarkus.oidc.application-type=service
%test.quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
smallrye.jwt.sign.key.location=/privateKey.pem

privateKey.pem 請從這裡取得 https://raw.githubusercontent.com/quarkusio/quarkus/main/integration-tests/oidc-tenancy/src/main/resources/privateKey.pem

Test User Resource


import io.quarkus.test.junit.QuarkusTest
import io.restassured.module.kotlin.extensions.Given
import io.restassured.module.kotlin.extensions.Then
import io.restassured.module.kotlin.extensions.When
import io.smallrye.jwt.build.Jwt
import org.hamcrest.CoreMatchers.`is`
import org.junit.jupiter.api.Test

@QuarkusTest
class UserResourceTest {

    @Test
    fun `test api me`() {
        Given {
            auth().oauth2(getAccessToken("alice"))
        } When {
            get("/api/users/me")
        } Then {
            statusCode(200)
            body("userName", `is`("alice"))
        }
    }

    private fun getAccessToken(userName: String): String {
        return Jwt.preferredUserName(userName)
            .issuer("https://server.example.com")
            .audience("https://service.example.com")
            .sign()
    }
}

測試也跑過了!!收功?

跑跑其他測試

發現其他測試失敗了,阿,因為都沒有驗證,明天來補補~


上一篇
使用 OIDC 達到 Quarkus 應用的 SSO (Single Sign-On)
下一篇
整合登入者資訊, 記錄是誰修改了資料, 用 Compose Either 達成吧
系列文
Quarkus, Kotlin, Reactive 雲原生服務開發32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言